/* A wee progam to test the spangling sockets. */

/* #define _P __P */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/time.h>

#ifdef USER_STACK
#include "libusernet.h"
#else
#define user_init() ((void)0)
#define user_socket(a,b,c) socket(a,b,c)
#define user_bind(a,b,c) bind(a,b,c)
#define user_connect(a,b,c) connect(a,b,c)
#define user_recv(a,b,c,d) recv(a,b,c,d)
#define user_close(a) close(a)
#define user_kill() ((void)0)
#endif


#define RECV_LENGTH 2048
#define RECV_SIZE   1400


long tvdif2us(struct timeval *t0, struct timeval *t1)
{
  struct timeval tdiff;
  long us;

  tdiff.tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff.tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff.tv_usec < 0)
    tdiff.tv_sec--, tdiff.tv_usec += 1000000;

  us = ((long)tdiff.tv_sec*1000000) + tdiff.tv_usec;
  return us;
}


int main(int   argc,
	 char *argv[])
{
    int                sock;
    struct sockaddr_in laddr;
    struct sockaddr_in raddr;
    char               buf[RECV_LENGTH];
    int                addrlen = sizeof(struct sockaddr_in);
    int                received = 1;
    long               total_received = 0;
    int                i;
    int                p, old_p;
    struct hostent    *he;
    int                ret;
    int                port;
    struct timeval a, b;

    user_init();

    if(argc != 4)
    {
	fprintf(stderr, "%s: Usage: %s <l. port> <r. host> <r. port>\n", 
		argv[0], argv[0]);
	exit(EXIT_FAILURE);
    }

    port = strtol(argv[1], NULL, 10);
    if(errno == ERANGE || port > 65535 || port < 0)
    {
	fprintf(stderr, "%s: port \"%s\" invalid.\n", argv[0], argv[1]);
	exit(EXIT_FAILURE);
    }    
    laddr.sin_addr.s_addr = htonl(INADDR_ANY);
    laddr.sin_port        = htons(port);
    laddr.sin_family      = AF_INET;

    port = strtol(argv[3], NULL, 10);
    if(errno == ERANGE || port > 65535 || port < 0)
    {
	fprintf(stderr, "%s: port \"%s\" invalid.\n", argv[0], argv[3]);
	exit(EXIT_FAILURE);
    }
    he = gethostbyname(argv[2]);
    if(he == NULL)
    {
	fprintf(stderr, "%s: failed to look up host \"%s\", reason: ", 
		argv[0], argv[2]);
	herror(NULL);
	fprintf(stderr, "\n");
	exit(EXIT_FAILURE);	
    }
    if(he->h_addrtype != AF_INET || he->h_length != 4)
    {
	fprintf(stderr, "%s: host \"%s\" has non-IPv4 address.\n", 
		argv[0], argv[3]);
	exit(EXIT_FAILURE);
    }
    raddr.sin_addr.s_addr = *((long *)(he->h_addr_list[0]));
    raddr.sin_port        = htons(port);
    raddr.sin_family       = AF_INET;

    printf("socket: \t");
    sock = user_socket(AF_INET, SOCK_DGRAM, 0);
    printf("%d\n", sock);
    if(sock < 0)
    {
	fprintf(stderr, "%s: socket failed: %s.\n",
		argv[0], strerror(errno));
	exit(EXIT_FAILURE);
    }

    printf("bind: \t\t");
    ret = user_bind(sock, 
		    (struct sockaddr *)&laddr, 
		    sizeof(struct sockaddr_in));
    printf("%d\n", ret);
    if(ret < 0)
    {
	fprintf(stderr, "%s: bind failed: %s.\n",
		argv[0], strerror(errno));
	exit(EXIT_FAILURE);
    }
#if 1
    ret = user_connect(sock, 
                       (struct sockaddr *)&raddr, 
                       sizeof(struct sockaddr_in));
    printf("connect: \t%d\n", ret);
    if(ret < 0)
    {
	fprintf(stderr, "%s: connect failed: %s.\n",
		argv[0], strerror(errno));
	exit(EXIT_FAILURE);
    }
#endif
    p = old_p = 0;
    i = 0;
    gettimeofday(&a, NULL);
    while ( 1 )
    { 
	received = user_recv(sock,
			     buf,
			     RECV_LENGTH,
			     0);
        if ( received == 0 ) continue;
	if ( received < 0 )
	{
	    fprintf(stderr, "%s: recv failed: %s.\n",
		    argv[0], strerror(errno));
	    printf("Total transfer: %ld.\n", total_received);
	    exit(EXIT_FAILURE);
	}
        if ( received != RECV_SIZE )
        {
            fprintf(stderr, "%s: packet was %d bytes.\n", argv[0], received);
            exit(EXIT_FAILURE);
        }
#if 0
        if ( *((int *)buf) != i++ )
        {
            fprintf(stderr, "%s: got %d when wanted %d.\n", 
                    argv[0], *((int *)buf), i-1);
            i = *((int *)buf) + 1;
        }
#endif
	total_received += received;

        if ( ((++p % 100) == 0) && 
             (tvdif2us(&a, (gettimeofday(&b, NULL), &b)) > 1000000) )
        {
            /*
             * This bandwidth estimator should output once a second has passed,
             * and a multiple of 5000 packets have been sent.
             */
            printf("%s (%ld.%ld): %.1f Mb/s, %dK pkts\n", argv[1],
                   b.tv_sec, b.tv_usec,
                   (((float)(p-old_p)*RECV_SIZE*8)/
                    ((float)tvdif2us(&a, &b))),
                   (p - old_p)/1000);
            fflush(stdout);
            gettimeofday(&a, NULL);
            old_p = p;
        }

        
    }
    printf("Total transfer: %ld.\n", total_received);
    
    printf("close: \t\t");
    ret = user_close(sock);
    printf("%d\n", ret);
    if(ret < 0)
    {
	fprintf(stderr, "%s: close failed: %s.\n",
		argv[0], strerror(errno));
	exit(EXIT_FAILURE);
    }		       

    user_kill();
 
    exit(EXIT_SUCCESS);
} 

/* End of $RCSFile$ */
